﻿/* Copyright (C) 2008-2009 Jorson.WHY
 * All right reserved
 * ********** Project Info ***********
 * Project Name : Common
 * NameSpace : MyLibrary.Tools
 * Filename : ProcessRunner
 * Create Time : 4/9/2008 9:37:03 AM
 * ********** Environment Info *******
 * Machine Name : EA-01504-11220
 * CLR Version : 2.0.50727.1433
 */

/* 调用外部进程时会遇到这样的两个问题
 * 1.当被调用的外部进程出现异常,出现异常后Windows会弹出错误报告框,程序当掉,必须手工关闭.
 * 2.当调用外部进程后,程序阻塞在Process.StandardOutput.ReadToEnd();被调用的进程也被阻塞.这是因为管道重定向后,调用程序没有及时将管道
 * 中的输出数据取出,结果导致管道阻塞.另外如果错误信息管道中的信息不及时取出,也会造成程序阻塞.这需要一个辅助线程来将信息取出.
 */
namespace MyLibrary.Tools
{
    #region System Using
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Diagnostics;
    using System.Threading;
    using System.Runtime.InteropServices;
    #endregion

    /// <summary>
    /// 进程启动类
    /// </summary>
    public class ProcessRunner
    {
        private string _error = String.Empty;
        public string Error
        {
            get { return _error; }
        }

        private string _output = String.Empty;
        public string Output
        {
            get { return _output; }
        }

        public bool HasError
        {
            get { return String.IsNullOrEmpty(this._error); }
        }

        /// <summary>
        /// 运行外部进程
        /// </summary>
        /// <param name="fileName">执行文件名称</param>
        /// <param name="showWindow">是否显示新窗口</param>
        /// <param name="args">参数</param>
        public void Run(string fileName, bool showWindow, params string[] args)
        {
            StringBuilder builder = new StringBuilder();
            try
            {
                Microsoft.Win32.RegistryKey key;
                key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"software\microsoft\PCHealth\ErrorReporting\", true);

                int doReport = (int)key.GetValue("DoReport");
                if (doReport != 0)
                    key.SetValue("DoReport", 0);

                int showUI = (int)key.GetValue("ShowUI");
                if (showUI != 0)
                    key.SetValue("ShowUI", 0);
            }
            catch { }

            try
            {
                string para = String.Empty;
                foreach (string arg in args)
                    para += arg;

                Process p = new Process();
                p.StartInfo.FileName = fileName;
                p.StartInfo.Arguments = para;
                p.StartInfo.UseShellExecute = false;
                p.StartInfo.RedirectStandardInput = true;
                p.StartInfo.RedirectStandardOutput = true;
                p.StartInfo.RedirectStandardError = true;
                p.StartInfo.CreateNoWindow = showWindow;

                p.Start();

                ErrorReader errorReader = new ErrorReader(p);
                while (!p.HasExited)
                    builder.Append(p.StandardOutput.ReadLine() + "\r\n");
                builder.Append(p.StandardOutput.ReadToEnd());

                while (!errorReader.HasExisted)
                    Thread.Sleep(1);
                _error = errorReader.Error;
                _output = builder.ToString();
            }
            catch(Exception ex)
            {
                _error = ex.Message;
            }
        }
    }

    class ErrorReader
    {
        Thread _thread = null;
        Process _process = null;
        static object _syncObj = new object();

        string _error = String.Empty;
        public string Error
        {
            get { return _error; }
            set { _error = value; }
        }

        bool _hasExisted = false;
        public bool HasExisted
        {
            get { lock(_syncObj) return _hasExisted; }
            set { lock(_syncObj) _hasExisted = value; }
        }

        private void ReadError()
        {
            StringBuilder builder = new StringBuilder();
            while (!_process.HasExited)
                builder.Append(_process.StandardError.ReadLine());
            builder.Append(_process.StandardError.ReadToEnd());

            _error = builder.ToString();
            HasExisted = true;
        }

        public ErrorReader(Process p)
        {
            HasExisted = false;
            this._process = p;
            this._thread = new Thread(new ThreadStart(ReadError));
            this._thread.Start();
        }
    }


}
